﻿//-----------------------------------------------------------------------
// <copyright file="ChangeListPicker.cs" company="Microsoft Corporation">
//     Copyright (c) Microsoft Corporation 2011. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

namespace Internal.TeamFoundationServer.Submit
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Drawing;
    using System.Linq;
    using System.Windows.Forms;
    using EnvDTE80;
    using Microsoft.TeamFoundation;
    using Microsoft.TeamFoundation.VersionControl.Client;
    using Microsoft.VisualStudio.CommandBars;
    using Properties;

    /// <summary>
    /// ChangeListSelector class.
    /// </summary>
    internal partial class ChangeListPicker : WizardControlTemplate
    {
        /// <summary>
        /// Pending Changes Conflicts Toolbar Toolbar title.
        /// </summary>
        private const string PendingChangesConflictsToolbar = "Pending Changes Conflicts Toolbar";

        /// <summary>
        /// Shelve set Comments dictionary.
        /// </summary>
        private readonly Dictionary<string, string> shelvesetComments = new Dictionary<string, string>();

        /// <summary>
        /// Holds the number of Team Projects the change list belongs to.
        /// </summary>
        private int projectCount;

        /// <summary>
        /// Check-in Notes object.
        /// </summary>
        private CheckinNotes checkinNotes;

        /// <summary>
        /// Initializes a new instance of the <see cref="ChangeListPicker"/> class.
        /// </summary>
        public ChangeListPicker()
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                this.InitializeComponent();

                // If we are not working with Code Coverage, hide code coverage column.
                if (!TfsSubmitConfig.ValidateCodeCoverageEnabled)
                {
                    var dataGridViewColumn = this.gridFiles.Columns["colCodeCoverage"];
                    if (dataGridViewColumn != null)
                    {
                        dataGridViewColumn.Visible = false;
                    }
                }

                this.cmbTFSServer.Items.Clear();
                foreach (var item in TeamFoundationHelper.GetRegisteredCollectionNames())
                {
                    this.cmbTFSServer.Items.Add(item);
                }

                Utilities.FixComboItemsWidth(this, this.cmbTFSServer);

                if (this.cmbTFSServer.Items.Count > 0)
                {
                    Utilities.GetDefaultValue(this.cmbTFSServer, Settings.Default.TfsSourceServer);
                }
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// Update window.
        /// </summary>
        /// <param name="title">Window Title.</param>
        /// <param name="description">Window Description.</param>
        /// <param name="backButtonEnabled">Back Button Enabled?</param>
        /// <param name="nextButtonEnabled">Next Button Enabled?</param>
        /// <param name="nextButtonCaption">Next Button Caption.</param>
        /// <param name="action">Action to perform on control.</param>
        public override void UpdateWindow(Label title, RichTextBox description, out bool backButtonEnabled, out bool nextButtonEnabled, out string nextButtonCaption, object action)
        {
            backButtonEnabled = true;
            nextButtonEnabled = this.gridFiles.RowCount > 0;
            nextButtonCaption = Resources.DefaultButtonNextCaption;

            title.Text = Resources.ChangeListPickerTitle;
            description.Text = Resources.ChangeListPickerDescription;
        }

        /// <summary>
        /// Continue button event.
        /// </summary>
        public override void NextStep()
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                if (this.gridFiles.Rows.Cast<DataGridViewRow>().Any(row => row.Cells["colProblems"].Value != null))
                {
                    /*
                    var window = Connect.VisualStudioObject.Windows.Item("{2456BD12-ECF7-4988-A4A6-67D49173F564}") as Window2;

                    // Show conflicts
                    if (window != null)
                    {
                        foreach (CommandBar commandBar in (CommandBars)window.CommandBars)
                        {
                            string caption = commandBar.Name.Replace("&", string.Empty);
                            if (caption.Contains(PendingChangesConflictsToolbar))
                            {
                                window.Visible = true;
                                break;
                            }
                        }
                    }
                     * */

                    MessageBox.Show(
                        Resources.ChangeListResolveProblemString,
                        Resources.ErrorString,
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error,
                        MessageBoxDefaultButton.Button1,
                        MessageBoxOptions.RightAlign & MessageBoxOptions.RtlReading);
                    return;
                }
                
                // TFS Submit only supports work with one Team Project at time
                // Validate if we have multiple team projects in our changes.
                if (this.projectCount > 1)
                {
                    MessageBox.Show(
                        Resources.ChangeListMultipleProjectsProblemString,
                        Resources.ErrorString,
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error,
                        MessageBoxDefaultButton.Button1,
                        MessageBoxOptions.RightAlign & MessageBoxOptions.RtlReading);

                    return;
                }
                
                // Validate Local Policies.
                var failures = TeamFoundationHelper.EvaluateCheckin(this.cmbTFSServer.Text, this.cmbWorkspace.Text, this.cmbShelveset.Text);
                if (!string.IsNullOrEmpty(failures))
                {
                    MessageBox.Show(
                           Resources.ChangeListResolveViolationsString + Environment.NewLine + failures,
                            Resources.ErrorString,
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Error,
                            MessageBoxDefaultButton.Button1,
                            MessageBoxOptions.RightAlign & MessageBoxOptions.RtlReading);

                    return;
                }

                // CheckinNote class will need to work with the workspace, so at this point we should make sure to provide there.
                CheckinNotes.Workspace = TeamFoundationHelper.GetWorkspace(this.cmbTFSServer.Text, this.cmbWorkspace.Text);

                // We also need to give shelve set name.
                CheckinNotes.ShelvesetName = this.cmbShelveset.Text;
                CheckinNotes.ServerSideCheckin = this.chkServerSide.Checked;

                string description;
                if (this.shelvesetComments.TryGetValue(this.cmbShelveset.Text, out description))
                {
                    CheckinNotes.ShelvesetDescription = description;
                }

                Debug.Assert(this.checkinNotes == null || this.checkinNotes.IsDisposed, "BUGBUG checkinNotes should be null or not disposed at this point.");

                this.checkinNotes = new CheckinNotes();
                this.FireOpenControl(this.checkinNotes);
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// Select TFS Collection.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void CmbTfsServerSelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                this.cmbWorkspace.Items.Clear();
                this.cmbShelveset.Items.Clear();
                this.gridFiles.Rows.Clear();

                if (this.cmbTFSServer.Text.Length == 0)
                {
                    return;
                }

                Utilities.SetDefaultValue("TfsSourceServer", this.cmbTFSServer.Text);

                // Retrieve User workspaces for specific project collection.
                foreach (var item in TeamFoundationHelper.GetWorkspaceNames(this.cmbTFSServer.Text))
                {
                    this.cmbWorkspace.Items.Add(item);
                }

                Utilities.FixComboItemsWidth(this, this.cmbWorkspace);

                if (this.cmbWorkspace.Items.Count > 0)
                {
                    Utilities.GetDefaultValue(this.cmbWorkspace, Settings.Default.TfsSourceWorkspace);
                }
            }
            catch (TeamFoundationServiceUnavailableException err)
            {
                MessageBox.Show(
                   err.Message,
                   Resources.UnhandledException,
                   MessageBoxButtons.OK,
                   MessageBoxIcon.Error,
                   MessageBoxDefaultButton.Button1,
                   MessageBoxOptions.RightAlign & MessageBoxOptions.RtlReading);
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// Select Workspace.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void CmbWorkspaceSelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                this.cmbShelveset.Items.Clear();
                this.gridFiles.Rows.Clear();

                if (this.cmbWorkspace.Text.Length == 0)
                {
                    return;
                }

                Utilities.SetDefaultValue("TfsSourceWorkspace", this.cmbWorkspace.Text);

                this.shelvesetComments.Clear();
                foreach (var shelveset in TeamFoundationHelper.GetShelvesets(this.cmbTFSServer.Text, this.cmbWorkspace.Text))
                {
                    this.cmbShelveset.Items.Add(shelveset.Name);
                    this.shelvesetComments.Add(shelveset.Name, shelveset.Comment);
                }

                Utilities.FixComboItemsWidth(this, this.cmbShelveset);

                if (this.cmbShelveset.Items.Count > 0)
                {
                    this.cmbShelveset.SelectedIndex = 0;
                }
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// Select Change list.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void CmbShelvesetSelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                this.projectCount = 0;
                this.gridFiles.Rows.Clear();

                if (this.cmbShelveset.Text.Length == 0)
                {
                    return;
                }

                // First we retrieve the list of all current pending changes and build a dictionary with it.
                // We do that to validate scenarios where file is not valid anymore. i.e: file was deleted by different user.
                var pendingChanges = TeamFoundationHelper.GetPendingChanges(this.cmbTFSServer.Text, this.cmbWorkspace.Text);
                if (pendingChanges == null)
                {
                    return;
                }

                CheckinNotes.PendingChanges.Clear();

                var items = pendingChanges.ToDictionary(pendingChange => pendingChange.ServerItem);

                // This list keep track the project names the change is related too.
                // TFS Submit only supports one Project Team at time.
                var project = new List<string>();

                // Before we add the list of files in the grid, retrieve the list of files for a specific shelve set.
                pendingChanges = TeamFoundationHelper.GetPendingChangesByShelveSetName(this.cmbTFSServer.Text, this.cmbWorkspace.Text, this.cmbShelveset.Text);

                foreach (var pendingChange in pendingChanges)
                {
                    // Verify if specific pending item exist in the global list of pending changes.
                    // if it does not exist, it means the user has a file in a shelve set that is not currently opened for edit on the workspace.
                    PendingChange pc = null;
                    if (this.chkServerSide.Checked || items.TryGetValue(pendingChange.ServerItem, out pc))
                    {
                        if (pc == null && this.chkServerSide.Checked)
                        {
                            pc = pendingChange;
                        }

                        // Retrieve the Project name and add it to the list
                        // We do that to count how many projects are involved on this change.
                        var projectName = TeamFoundationHelper.GetTeamProjectNameByLocalItem(this.cmbTFSServer.Text, this.cmbWorkspace.Text, pc.LocalOrServerItem);
                        if (!string.IsNullOrEmpty(projectName) && !project.Contains(projectName))
                        {
                            project.Add(projectName);
                        }

                        // Add pending changes in checkinNotes static object since it will need this info in the next step.
                        CheckinNotes.PendingChanges.Add(pc);
                        
                        // Retrieve current code coverage
                        var codeCoverage = CodeCoverage.GetCurrentCodeCoverage(pc.LocalOrServerFolder, pc.LocalOrServerItem, pc.FileName);

                        this.gridFiles.Rows.Add(pc.ChangeTypeName, pc.FileName, pc.LocalOrServerFolder, codeCoverage == -1 ? "N/A" : codeCoverage + "%", null, pc.ServerItem);
                        if (string.IsNullOrEmpty(projectName))
                        {
                            this.gridFiles.Rows[this.gridFiles.Rows.Count - 1].Cells["colProblems"].Value = "File not associated with any TFS project.";
                            this.gridFiles.Rows[this.gridFiles.Rows.Count - 1].DefaultCellStyle.BackColor = Color.Red;
                            continue;
                        }

                        if (codeCoverage > -1 && codeCoverage < 70)
                        {
                            this.gridFiles.Rows[this.gridFiles.Rows.Count - 1].Cells["colProblems"].Value = "File does not meet the {0}% code coverage bar. Please add more tests before check-in this file.";
                            this.gridFiles.Rows[this.gridFiles.Rows.Count - 1].DefaultCellStyle.BackColor = Color.Red;
                            continue;
                        }
                    }
                    else
                    {
                        // File does not exist, mark file as RED
                        this.gridFiles.Rows.Add(pendingChange.ChangeTypeName, pendingChange.FileName, pendingChange.LocalOrServerFolder, "N/A", null, pendingChange.ServerItem);
                        this.gridFiles.Rows[this.gridFiles.Rows.Count - 1].Cells["colProblems"].Value = "File not pending for check-in in this workspace.";
                        this.gridFiles.Rows[this.gridFiles.Rows.Count - 1].DefaultCellStyle.BackColor = Color.Red;
                        continue;
                    }

                    // Validate if Pending item has any current conflict.
                    var conflict = TeamFoundationHelper.GetItemConflictStatus(this.cmbTFSServer.Text, this.cmbWorkspace.Text, pendingChange);
                    if (string.IsNullOrEmpty(conflict))
                    {
                        continue;
                    }

                    this.gridFiles.Rows[this.gridFiles.Rows.Count - 1].Cells["colProblems"].Value = conflict;
                    this.gridFiles.Rows[this.gridFiles.Rows.Count - 1].DefaultCellStyle.BackColor = Color.Red;
                }

                // Update projectCount with the number of projects we are dealing on this change list.
                this.projectCount = project.Count;
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// Rows Added event.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void GridFilesRowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
        {
            this.FireChangeContinueButtonEnabled(this.gridFiles.RowCount > 0);
        }

        /// <summary>
        /// Rows Removed event.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void GridFilesRowsRemoved(object sender, DataGridViewRowsRemovedEventArgs e)
        {
            this.FireChangeContinueButtonEnabled(this.gridFiles.RowCount > 0);
        }

        /// <summary>
        /// Right mouse click event.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void GridFilesMouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                // TODO Add context menu.
                // Context menu will enabled user to try to merge changes.
            }
        }

        /// <summary>
        /// Server side checked changed.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void ChkServerSideCheckedChanged(object sender, EventArgs e)
        {
            this.CmbShelvesetSelectedIndexChanged(sender, e);
        }
    }
}
